package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 报警规则Service业务层处理
 * 
 * @author zbr
 * @date 2025-03-11
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private IAlertDataService alertDataService;

    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return alertRuleMapper.selectById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return alertRuleMapper.insert(alertRule);
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return alertRuleMapper.updateById(alertRule);
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return alertRuleMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return alertRuleMapper.deleteById(id);
    }

    /**
     * 通过报警规则过滤记录数据
     */
    @Override
    public void alertFilter() {
        log.warn("记录报警数据");
        // 1.查询所有报警规则
        Long count = lambdaQuery().eq(AlertRule::getStatus, 1).count();
        if (count == 0) {
            return;
        }
        // 2.查询所有设备数据
        List<List<DeviceData>> values = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if (CollUtil.isEmpty(values)) {
            return;
        }
        List<DeviceData> deviceDataList = new ArrayList<>();
        // values.forEach(item -> {
        //     deviceDataList.addAll(item);
        // });
        values.forEach(deviceDataList::addAll);

        // deviceDataList.forEach(item -> {
        //     alertFilterEveryData(item);
        // });
        // 3.遍历设备数据，判断是否满足报警规则
        LocalDateTime now = LocalDateTime.now();
        deviceDataList.forEach(item -> {
            alertFilterEveryData(item, now);
        });
    }

    /**
     * 单条数据过滤
     *
     * @param deviceData
     * @param now
     */
    private void alertFilterEveryData(DeviceData deviceData, LocalDateTime now) {
        // 1.判断数据上报数据是否在1个周期(分钟)内
        LocalDateTime alarmTime = deviceData.getAlarmTime();
        if (alarmTime.plusMinutes(1).isBefore(now)) {
            return;
        }
        // 2.查询全部此设备的报警规则
        List<AlertRule> allRules = lambdaQuery()
                .eq(AlertRule::getStatus, 1)
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .eq(AlertRule::getIotId, -1).list();
        // 3.查询特定此设备的报警规则
        List<AlertRule> deviceRules = lambdaQuery()
                .eq(AlertRule::getStatus, 1)
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .eq(AlertRule::getIotId, deviceData.getIotId()).list();
        // 4.组合规则
        Collection<AlertRule> allAlertRules = CollUtil.addAll(allRules, deviceRules);
        if (CollUtil.isEmpty(allAlertRules)) {
            return;
        }
        // 5.遍历规则，判断是否满足规则
        allAlertRules.forEach(item -> {
            dataMatchRules(item, deviceData);
        });
    }

    /**
     * 数据匹配规则
     * @param alertRule
     * @param deviceData
     */
    private void dataMatchRules(AlertRule alertRule, DeviceData deviceData) {
        // 1.判断是否在生效时间内
        String[] time = alertRule.getAlertEffectivePeriod().split("~");
        LocalTime start = LocalTime.parse(time[0]);
        LocalTime end = LocalTime.parse(time[1]);
        LocalTime alertTime = deviceData.getAlarmTime().toLocalTime();
        if (alertTime.isBefore(start) || alertTime.isAfter(end)) {
            return;
        }
        // 2.检验数据是否达到阈值
        Double value = alertRule.getValue();
        Double dataValue = Double.valueOf(deviceData.getDataValue());
        // value大返回1 小返回-1 相同返回0
        int result = Double.compare(dataValue, value);
        //统计次数的key
        String iotId = deviceData.getIotId();
        String aggCountKey = CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + alertRule.getId() + ":" + iotId + ":" + deviceData.getFunctionId();
        if ((">=".equals(alertRule.getOperator()) && result < 0)
            || ("<".equals(alertRule.getOperator()) && result >= 0)) {
            redisTemplate.delete(aggCountKey);
            return;
        }
        // 3.判断是否在沉默周期内
        String silentKey = CacheConstants.ALERT_SILENT_PREFIX + alertRule.getId() + ":" + iotId + ":" + deviceData.getFunctionId();
        Object silentData = redisTemplate.opsForValue().get(silentKey);
        if (ObjUtil.isNotNull(silentData)) {
            return;
        }
        // 4.判断当前报警次数是否等于持续周期
        Integer aggCount = (Integer) redisTemplate.opsForValue().get(aggCountKey);
        aggCount = aggCount == null ? 1 : aggCount + 1;
        if (!aggCount.equals(alertRule.getDuration())) {
            redisTemplate.opsForValue().set(aggCountKey, aggCount);
            return;
        }
        redisTemplate.delete(aggCountKey);
        redisTemplate.opsForValue().set(silentKey, 1, alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);
        // 5.查询设备管理人员
        List<Long> userIds = new ArrayList<>();
        if (alertRule.getAlertDataType() == 0) {
            if (deviceData.getLocationType() == 0) {
                userIds = deviceMapper.selectNursingIdsByIotIdWithElder(iotId);
            } else if (deviceData.getLocationType() == 1) {
                userIds = deviceMapper.selectNursingIdsByIotIdWithBed(iotId);
            }
        } else {
            userIds = sysUserRoleMapper.selectUserIdByRoleName("维修工");
        }
        // 去除null
        userIds = userIds.stream().filter(Objects::nonNull).collect(Collectors.toList());
        List<Long> superAdminIds = sysUserRoleMapper.selectUserIdByRoleName("超级管理员");
        // 合并去重userIds和superAdminIds
        List<Long> allUserIds = CollUtil.addAll(userIds, superAdminIds).stream().distinct().collect(Collectors.toList());
        // 6.插入报警数据
        insertBranchAlertData(alertRule, deviceData, allUserIds);
        // 7.发送报警通知
        sendAlertMessage(alertRule, deviceData, allUserIds);
    }

    /**
     * 发送报警通知
     * @param alertRule
     * @param deviceData
     * @param allUserIds
     */
    private void sendAlertMessage(AlertRule alertRule, DeviceData deviceData, List<Long> allUserIds) {
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(deviceData, AlertNotifyVo.class);
        alertNotifyVo.setFunctionName(alertRule.getFunctionName());
        alertNotifyVo.setAlertDataType(alertRule.getAlertDataType());
        alertNotifyVo.setVoiceNotifyStatus(0);
        alertNotifyVo.setNotifyType(1);
        if (deviceData.getLocationType() == 1 && deviceData.getPhysicalLocationType() == 2) {
            alertNotifyVo.setIsAllConsumer(true);
        } else {
            alertNotifyVo.setIsAllConsumer(false);
        }
        webSocketServer.sendMessageToConsumer(alertNotifyVo, allUserIds);
    }

    /**
     * 插入报警数据
     * @param alertRule
     * @param deviceData
     * @param allUserIds
     */
    private void insertBranchAlertData(AlertRule alertRule, DeviceData deviceData, List<Long> allUserIds) {
        List<AlertData> dataList = allUserIds.stream().map(userId -> {
            AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
            alertData.setId(null);
            alertData.setAlertRuleId(alertRule.getId());
            String alertReason = StrUtil.format("{}{}{},持续了{}周期报警", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
            alertData.setAlertReason(alertReason);
            alertData.setType(alertRule.getAlertDataType());
            alertData.setStatus(0);
            alertData.setUserId(userId);
            return alertData;
        }).collect(Collectors.toList());
        alertDataService.saveBatch(dataList);
    }


}
